;;; test-org-roam-node.el --- Tests for Org-roam -*- lexical-binding: t; -*-

;; Copyright (C) 2020 Jethro Kuan

;; Author: Jethro Kuan <jethrokuan95@gmail.com>
;; Package-Requires: ((buttercup))

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:
;;; Code:

(require 'buttercup)
(require 'org-roam)

(defvar root-directory default-directory)

(describe "org-roam-node-from-id"
  (before-all
    (setq org-roam-directory (expand-file-name "tests/roam-files")
          org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
          org-roam-file-extensions '("org")
          org-roam-file-exclude-regexp nil)
    (org-roam-db-sync))

  (after-all
    (org-roam-db--close)
    (delete-file org-roam-db-location))

  (it "returns nil for unknown id"
    (expect (org-roam-node-from-id "non-existing") :to-equal nil))

  (it "returns correct node from id"
    (let ((node (org-roam-node-from-id "884b2341-b7fe-434d-848c-5282c0727861")))
      (expect (org-roam-node-title node) :to-equal "Foo"))))

(describe "org-roam-node-from-title-or-alias"
  (before-all
    (setq org-roam-directory (expand-file-name "tests/roam-files")
          org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
          org-roam-file-extensions '("org")
          org-roam-file-exclude-regexp nil)
    (org-roam-db-sync))

  (after-all
    (org-roam-db--close)
    (delete-file org-roam-db-location))

  (it "returns nil for unknown title"
    (expect (org-roam-node-from-title-or-alias "non-existing") :to-equal nil))

  (it "returns correct node from title"
    (let ((node (org-roam-node-from-title-or-alias "Foo")))
      (expect (org-roam-node-title node) :to-equal "Foo")))

  (it "returns correct node from alias"
    (let ((node (org-roam-node-from-title-or-alias "Batman")))
      (expect (org-roam-node-title node) :to-equal "Bruce Wayne")))

  (it "returns correct node from alias with nocase"
    (let ((node (org-roam-node-from-title-or-alias "batman" t)))
      (expect (org-roam-node-title node) :to-equal "Bruce Wayne"))))

(describe "org-roam-demote-entire-buffer"
  (it "demotes an entire org buffer"
    (cd root-directory)
    (org-roam-with-temp-buffer "tests/roam-files/demoteable.org"
      (org-roam-demote-entire-buffer)
      (expect (buffer-substring-no-properties (point) (point-max))
              :to-equal
              "* Demoteable\n:PROPERTIES:\n:ID: 97bf31cf-dfee-45d8-87a5-2ae0dabc4734\n:END:\n\n** Demoteable h1\n\n*** Demoteable child\n"))))

(describe "org-roam-promote-entire-buffer"
  (it "at least succeeds without error signals"
    (cd root-directory)
    (org-roam-with-temp-buffer "tests/roam-files/promoteable.org"
      (expect (org-roam--promote-entire-buffer-internal)
              :not :to-throw 'error)))

  (it "is the perfect inverse of demoting, for certain simple file contents"
    (cd root-directory)
    (org-roam-with-temp-buffer "tests/roam-files/demoteable.org"
      (let ((original-contents (buffer-string)))
        (org-roam-demote-entire-buffer)
        (org-roam--promote-entire-buffer-internal)
        (equal original-contents (buffer-string))))))

(describe "org-roam-node-list"
  (before-all
    (setq org-roam-directory (expand-file-name "tests/roam-files")
          org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
          org-roam-file-extensions '("org")
          org-roam-file-exclude-regexp nil)
    (org-roam-db-sync))

  (it "returns the correct number of node objects"
    ;; Not equal to number of rows in nodes table,
    ;; because it instantiates an extra `org-roam-node' object per alias.
    (expect (length (org-roam-node-list)) :to-equal 30)))

(describe "org-roam--h1-count"
  (after-each
    (cd root-directory))

  (it "returns the correct number of level-1 headings"
    (find-file "tests/roam-files/foo.org" nil)
    (expect (org-roam--h1-count) :to-equal 0)

    (cd root-directory)

    (find-file "tests/roam-files/family.org" nil)
    (expect (org-roam--h1-count) :to-equal 1)))

(describe "org-roam--buffer-promoteable-p"
  (after-each
    (cd root-directory))

  (it "should check if buffer is promoteable"
    (find-file "tests/roam-files/foo.org" nil)
    (expect (org-roam--buffer-promoteable-p) :to-equal nil)

    (cd root-directory)

    (find-file "tests/roam-files/family.org" nil)
    (expect (org-roam--buffer-promoteable-p) :to-equal nil)

    (cd root-directory)

    (find-file "tests/roam-files/promoteable.org" nil)
    (expect (org-roam--buffer-promoteable-p) :to-equal t)))

(describe "org-roam--get-titles"
  (before-all
    (setq org-roam-directory (expand-file-name "tests/roam-files")
          org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
          org-roam-file-extensions '("org")
          org-roam-file-exclude-regexp nil)
    (org-roam-db-sync))

  (after-all
    (org-roam-db--close)
    (delete-file org-roam-db-location))

  (it "returns the list of titles and aliases"
    (let ((expected '(;; roam-files/foo.org
                      "Foo"
                      ;; roam-files/bar.org
                      "Bar"
                      ;; roam-files/with-alias.org
                      "Batman"
                      "The Dark Knight"
                      "Bruce Wayne"
                      ;; roam-files/demoteable.org
                      "Demoteable"
                      ;; roam-files/promoteable-with-id.org
                      "[1/2] Promoteable h1"
                      "[100%] Promoteable child"
                      ;; roam-files/family.org
                      "Family"
                      "Grand-Parent"
                      "Parent"
                      "Child"
                      ;; roam-files/ref_with_space.org
                      "ref with space"
                      ;; roam-files/with-times.org
                      "With Times"
                      "Scheduled heading"
                      "Deadline heading"
                      "Full planning-line"
                      "With CLOSED but no \"DONE\""
                      ;; roam-files/alternative-id-methods.org
                      "With =org-id-method= set to =org="
                      "With =org-id-method= set to =ts="
                      ;; roam-files/roam-exclude.org
                      "Another excluded node" ;; FIXME: !
                      "Not excluded"
                      "Also not excluded"
                      ;; roam-files/title-transformations.org
                      "A title with an embedded link"
                      "Title linking to A title with an embedded link"
                      "[100%] A title with a TODO-state, priority and [10/10] multiple statistics-cookies [10/10]"
                      "TODO A title that appears on first glance to have a TODO-state"
                      "A title with /italics/, *bold*, _underline_, +strikethrough+ and =monospace="
                      ;; roam-files/subdirectory/node-in-subdirectory.org
                      "A node in a subdirectory"
                      ;; roam-files/dailies/2025-11-11.org
                      "2025-11-11")))
      (expect (length (org-roam--get-titles)) :to-equal (length expected))
      (expect (org-roam--get-titles) :to-have-same-items-as expected))))

(describe "org-roam-alias"
  (before-all
    (setq org-roam-directory (expand-file-name "tests/roam-files")
          org-roam-db-location (expand-file-name "org-roam.db" temporary-file-directory)
          org-roam-file-extensions '("org")
          org-roam-file-exclude-regexp nil)
    (org-roam-db-sync))

  (after-all
    (org-roam-db--close)
    (delete-file org-roam-db-location)
    (cd root-directory))

  (it "adds an alias to a node"
    (cd root-directory)
    (find-file "tests/roam-files/foo.org" nil)
    (org-roam-alias-add "qux")
    (expect (buffer-substring-no-properties (point) (point-max))
            :to-equal ":PROPERTIES:\n:ID:       884b2341-b7fe-434d-848c-5282c0727861\n:ROAM_ALIASES: qux\n:END:\n#+title: Foo\n"))

  (it "removes an alias from a node"
    (cd root-directory)
    (find-file "tests/roam-files/with-alias.org" nil)
    (org-roam-alias-remove "Batman")
    (expect (buffer-substring-no-properties (point) (point-max))
            :to-equal ":PROPERTIES:\n:ID: 57ff3ce7-5bda-4825-8fca-c09f523e87ba\n:ROAM_ALIASES: \"The Dark Knight\"\n:END:\n#+title: Bruce Wayne\n")))

(describe "org-roam-node-slug"
  (it "transforms the title as intended"
    (expect (org-roam-node-slug (org-roam-node-create :title "How to convince me that 2 + 2 = 3"))
            :to-equal "how_to_convince_me_that_2_2_3")
    (expect (org-roam-node-slug (org-roam-node-create :title "Pros/cons of A/B-testing"))
            :to-equal "pros_cons_of_a_b_testing")
    (expect (org-roam-node-slug (org-roam-node-create :title "Löb's Theorem"))
            :to-equal "lob_s_theorem")
    (expect (org-roam-node-slug (org-roam-node-create :title "Where's #Waldo? Answer: \"Nowhere\"."))
            :to-equal "where_s_waldo_answer_nowhere")
    (expect (org-roam-node-slug (org-roam-node-create :title "中文 (zhōngwén)"))
            :to-equal "中文_zhongwen")
    (expect (org-roam-node-slug (org-roam-node-create :title "ローマ字 (rōmaji)"))
            :to-equal "ローマ字_romaji")
    ;; Wow! I don't know Arabic, but it must harder to read with the underscores!  --meedstrom
    (expect (org-roam-node-slug (org-roam-node-create :title "نص من اليمين إلى اليسار (right-to-left script)"))
            :to-equal "نص_من_اليمين_إلى_اليسار_right_to_left_script")))

(provide 'test-org-roam-node)

;;; test-org-roam-node.el ends here
